package app

import (
	"fmt"
	"github.com/coreos/etcd/clientv3"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v7"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"github.com/nsqio/go-nsq"
	"google.golang.org/grpc"
	"io"
	"log"
	"math/rand"
	"os"
	"time"
)

var db *gorm.DB

var orderDB = make([]*gorm.DB, 2)
var orderBinaryTreeDB = make(map[string]*gorm.DB, 8)

var cache *redis.Client
var mq *nsq.Producer
var mqs = make([]*nsq.Producer, 2)
var etcd *clientv3.Client


var orderDBConns = []conn {
	{user:"admin123", password:"Lingmaimai001", host:"pc-bp1vz6jcr4d78l8b5.mysql.polardb.rds.aliyuncs.com:3306", db:"mall_1"},
	{user:"admin123", password:"Lingmaimai001", host:"pc-bp16466uvky8avp56.mysql.polardb.rds.aliyuncs.com:3306", db:"mall_2"},
}

var orderBinaryTreeDBConns = map[string]conn {
	"db1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_1"},
	"db2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_2"},
	"db3" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_3"},
	"db4" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_4"},
	"db5" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_5"},
	"db6" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_6"},
	"db7" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_7"},
	"db8" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_8"},
}

var orderBinaryTreeDBTablesConns = map[string]conn {
	"db1_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_1"},
	"db1_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_1"},
	"db2_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_2"},
	"db2_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_2"},
	"db3_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_3"},
	"db3_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_3"},
	"db4_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_4"},
	"db4_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_4"},
	"db5_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_5"},
	"db5_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_5"},
	"db6_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_6"},
	"db6_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_6"},
	"db7_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_7"},
	"db7_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_7"},
	"db8_1" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_8"},
	"db8_2" : {user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall_8"},
}

var nsqConns = []string {
	"172.16.58.112:4150",
	"172.16.58.112:4150",
}

type conn struct {
	user string
	password string
	host string
	db string
}

func init() {
	rand.Seed(time.Now().UnixNano())

	initGin()
	initDB()
	//initCache()
	//initMQ()
	//initEtcd()
}

func GetDB() *gorm.DB {
	return db
}

func GetOrderDB() []*gorm.DB {
	return orderDB
}

func GetOrderBinaryTreeDB() map[string]*gorm.DB {
	return orderBinaryTreeDB
}


func GetCache() *redis.Client {
	return cache
}

func GetMQ() *nsq.Producer {
	return mq
}

func GetMQS() []*nsq.Producer {
	return mqs
}

func GetEtcd() *clientv3.Client {
	return etcd
}

func initDB() {

	conn := conn{user:"admin123", password:"Lingmaimai001", host:"mysql8.rwlb.rds.aliyuncs.com:3306", db:"mall"}
	db = initMySQL(conn, 40, 40)

	//for i, conn := range orderDBConns {
	//	orderDB[i] = initMySQL(conn, 40, 40)
	//}

	// 二叉树分库
	//for k, conn := range orderBinaryTreeDBConns {
	//	orderBinaryTreeDB[k] = initMySQL(conn, 40)
	//}
}

func initMySQL(conn conn, idleConns int, openConns int) *gorm.DB {
	connStr := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s",
		conn.user,
		conn.password,
		conn.host,
		conn.db,
	)

	var dbN *gorm.DB
	var err error
	if dbN, err = gorm.Open("mysql", connStr); err != nil {
		log.Fatalf("Fail to connect db : %v", err)
	}

	//defer DB.Close()

	dbN.LogMode(false)
	//默认加s， grom转义struct名字的时候不用加上s
	//db.SingularTable(true)

	// idle 空闲的
	dbN.DB().SetMaxIdleConns(idleConns)
	dbN.DB().SetMaxOpenConns(openConns)

	return dbN
}

func initGin() {
	gin.SetMode(gin.ReleaseMode)
	f, err := os.OpenFile("gin.log", os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModeAppend)
	if err != nil {
		fmt.Println("err", err)
	}
	gin.DefaultWriter = io.MultiWriter(f)
}



func initCache() {
	cache = redis.NewClient(&redis.Options{
		Addr:     "r-bp1p9ruvdd9o5l8pu8.redis.rds.aliyuncs.com:6379",
		Password: "",
		DB:       0,
	})

	_, err := cache.Ping().Result()
	if err != nil {
		panic(err)
	}

	//fields := make(map[string]interface{})
	//fields["id"] = 1
	//fields["stock"] = 10000
	//err = cache.HMSet("goods:1", fields).Err()
	//
	//if err != nil {
	//	panic(err)
	//}
}

func initMQ() {
	conn := "172.16.58.112:4150"
	mq = initNsq(conn)

	for i, conn := range nsqConns {
		mqs[i] = initNsq(conn)
	}
}

func initNsq(conn string) *nsq.Producer{
	config := nsq.NewConfig()
	mq, err := nsq.NewProducer(conn, config)
	if err != nil {
		log.Panic("nsq", err)
	}
	return mq
}

func initEtcd() {
	var err error
	etcd, err = clientv3.New(clientv3.Config{
		Endpoints:   []string{"39.100.58.10:2379"},
		DialTimeout: 3 * time.Second,
		DialOptions: []grpc.DialOption{grpc.WithBlock()},
	})

	if err != nil {
		panic(err)
	}
}

func Shutdown() {
	//cache.Close()
	mq.Stop()
	//etcd.Close()
	db.DB().Close()

	for _, mq := range mqs {
		mq.Stop()
	}

	for _, db := range orderDB {
		db.DB().Close()
	}

	//for _, db := range orderBinaryTreeDB {
	//	db.DB().Close()
	//}
}